4
移动开发中一定会涉及后台API如何访问,如何控制访问权限,保证系统安全等问题。
介绍一下我最近自己做的一个移动端访问后端API的例子,例子写的很粗糙,但是基本实现了以token auth 为核心的demo。

在实现demo过程中也找到了不少的好资料,这一篇《APP中用户验证方案》详细的介绍了APP用户验证的不同方案。

大体思路:

APP登录界面输入用户信息登录;服务端验证登录信息,验证成功,向APP端发送token,将token保存到数据库或者缓存服务器中;APP登录成功后,每次调用后端API时都需要带着token、时间戳、sign(token+时间戳的md5字符串,sign可以自己定义算法以防止被盗用)信息,以便后台验证访问API权限;后端在接收到APP访问请求时,比对APP发送来的token与缓存服务器中已经存在的token是否一致,并验证token时效性。

好了,废话少说,上代码:

前端代码(APP,最近写的APP基本都是H5方式实现):

<label>用户名</label>
        <input type="text" name="username" id="username" value="" />
        <br/>
        <label>密码</label>
        <input type="password" name="password" id="password" value="" />
        <input type="button" name="login" id="login" value="登录" />
        
        
        <script type="text/javascript">
            document.getElementById("login").addEventListener('click',function () {
                //alert('hello');
                //ajax提交验证信息到后端
                var username = document.getElementById("username").value;
                var password = document.getElementById("password").value;
                var data = "{\"username\":\"" + username +"\",\"password\":\"" + password + "\"}";
                var url = "/AppTest/loginServlet";
                var wd = "123";
                var headData = ["123","123456789012345","gsfgfgbdf"];
                postData(url,data, headData,function (backdata) {
                    if(backdata != null){
                        console.log("返回的信息为:  " + backdata);
                    }
                },wd);
                
            });
        </script>

这里的ostData使用的是封装好的Ajax函数,下面一并放出代码:

ajax.js:

/*对ajax进行简易封装,便于每次调用,省去参数设置*/
function postData(url, data, headData, callback, waitingDialog) {
    mui.ajax(url,{  
        data:data,  
        dataType:'json',  
        type:'post',
        headers: {
            "token" : headData[0],
            "timesamp" : headData[1],
            "sign" : headData[2]
        },
        contentType:"application/x-www-form-urlencoded; charset=utf-8",  
        timeout:20000,  
        success:callback,
        error:function(xhr,type,errorThrown){  
            //waitingDialog.close();
            alert("<网络连接失败,请重新尝试一下>", "错误", "OK", null);  
        }  
    });
}  
ajax中加上了headers参数,这里加headers参数的目的是,后台使用servlet+filter的方式控制访问,如果将token等其他参数直接和表单数据同时提交,filter在获取token信息后,servlet就不能获取到表单数据,这就比较尴尬了,或者后台使用AOP的方式控制访问权限 ,这个还没有来得及尝试。如果我能把token信息加到于表单数据不同的存储区域就好了,Google后找到了可以把数据加到请求的header中可以实现将token数据与表单数据分离的效果。

过滤器(Java实现 JDK1.8 Tomcat8.5.6):

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;

import me.wlc.wx.web.tool.Md5;

/**
 * 使用注解标注过滤器
 * @WebFilter将一个实现了javax.servlet.Filte接口的类定义为过滤器
 * 属性filterName声明过滤器的名称,可选
 * 属性urlPatterns指定要过滤 的URL模式,也可使用属性value来声明.(指定要过滤的URL模式是必选属性)
 * urlPatterns="/*" 表示过滤掉所有请求
 */
@WebFilter(filterName="AccessFilter",urlPatterns="/*")
public class AccessFilter implements Filter {

    @Override
    public void destroy() {
        
        System.out.println("过滤器销毁");

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        System.out.println("执行过滤操作");
        HttpServletRequest req = (HttpServletRequest) request;
        String uri = req.getRequestURI();
        System.out.println("uri is :  " + uri);
        
        //对请求的uri(即api)进行判断,如果是登录的uri则直接放行,如果是其他api则对sign进行验证操作
        if( !uri.endsWith("loginServlet") ){
            //从请求的url中取出token、时间戳、sign
            String token = req.getHeader("token");
            String timesamp = req.getHeader("timesamp");
            String sign = req.getHeader("sign");
            System.out.println("sign is :  " + sign);
            
            StringBuffer requestUrl = req.getRequestURL();
            System.out.println("请求的Url是:   " + requestUrl);
            
            //对token、timesamp 进行md5计算
            String signMd5 = Md5.getMD5(token + timesamp);
            if(sign.equals(signMd5)){
                //签名通过
                chain.doFilter(request, response);
            }else{
                //签名不通过,向app后端发送错误信息,提示重新登录
                
            }    
        }else{
            //登录操作
            chain.doFilter(request, response);
        }
        
        //请求通过
        //chain.doFilter(request, response);
        
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
        
        System.out.println("过滤器初始化");
        
    }

}

后端还没有实现验证token时效性功能,也没有将token存储到缓存服务器中,这些需要具体方案定下来以后再加上了。再说一句,生成的token保证token值唯一即可,可以使用最方便的UUID(Java中)。

大体就是这样了,有问题随时提给我哦!




PS:2018年9月28日更新
一直没有登录查看留言,给留言板的各位兄弟道个歉,没有及时回复

token存储方案:

方案一:使用session的超时时间来控制token的失效时间
方案二:使用redis存储token的失效时间

方案一适用于单机环境,可以作为小型系统的token时效性验证方案,此方案若处于分布式环境会导致session在分布式机器间的时间不同步,但跟组里的其他工程师沟通,在分布式环境下是不是还可以同步session?

方案二适用于大型系统,还要使用redis服务,使用redis存储token可是实现快速读取token时效数据,若将toke失效时间存储到数据库中,频繁操作数据库,会影响数据库的响应时间,进而应用程序也会出现卡顿等情况。

另,token的失效时间应该设置多长需要根据所在项目的实际情况确定。


912093517
38 声望2 粉丝

游走于前端后端之间,浮云流水,奈何菜鸟一只